#include "trackball.h"

Trackball::Trackball(float radius) 
    : m_radius(radius), m_windowWidth(800), m_windowHeight(600),
      m_currentRotation(1.0f, 0.0f, 0.0f, 0.0f), 
      m_lastRotation(1.0f, 0.0f, 0.0f, 0.0f),
      m_isDragging(false) {
}

void Trackball::setRadius(float radius) {
    m_radius = radius;
}

void Trackball::setWindowSize(int width, int height) {
    m_windowWidth = width;
    m_windowHeight = height;
}

void Trackball::startDrag(int x, int y) {
    m_startPoint = mapToSphere(x, y);
    m_lastRotation = m_currentRotation;
    m_isDragging = true;
}

void Trackball::drag(int x, int y) {
    if (!m_isDragging) return;
    
    glm::vec3 currentPoint = mapToSphere(x, y);
    float x0 =currentPoint.x;
    float y0 = currentPoint.y;
    float z0 = currentPoint.z;
    
    // 计算旋转轴 (两向量叉乘)
    glm::vec3 axis = glm::cross(m_startPoint, currentPoint);
    
    // 计算旋转角度 (两向量点乘)
    float dot = glm::dot(m_startPoint, currentPoint);
    // 限制点乘结果在[-1,1]范围内，防止数值误差导致的问题
    dot = glm::clamp(dot, -1.0f, 1.0f);
    float angle = 2.0f * glm::acos(dot);
    
    // 创建旋转四元数
    if (glm::length(axis) > 0.001f) { // 避免零向量
        axis = glm::normalize(axis);
        glm::quat rotation = glm::angleAxis(angle, axis);
        m_currentRotation = rotation * m_lastRotation;
        m_currentRotation = glm::normalize(m_currentRotation);
    }
}

glm::mat4 Trackball::getRotationMatrix() const {
    return glm::mat4_cast(m_currentRotation);
}

void Trackball::reset() {
    m_currentRotation = glm::quat(1.0f, 0.0f, 0.0f, 0.0f);
    m_lastRotation = glm::quat(1.0f, 0.0f, 0.0f, 0.0f);
    m_isDragging = false;
}

glm::vec3 Trackball::mapToSphere(int x, int y) const {
    // 将屏幕坐标转换为[-1,1]范围内的标准化设备坐标
    float nx = 2.0f * x / m_windowWidth - 1.0f;
    float ny = 1.0f - 2.0f * y / m_windowHeight;
    
    // 计算到中心的距离
    float r = nx * nx + ny * ny;
    
    glm::vec3 point;
    
    if (r <= 1.0f) { // 在球体内部
        point = glm::vec3(nx, ny, glm::sqrt(1.0f - r));
    } else { // 在球体外部，使用双曲表面上的点
        float s = 1.0f / glm::sqrt(r);
        point = glm::vec3(nx * s, ny * s, 0.0f);
    }
    
    return point;
}    